#pragma once

#include <iostream>
#include <string>
#include <unistd.h>
#include <vector>
#include <queue>

#include "Thread.hpp"
#include "Task.hpp"
#include "LockGuard.hpp"
#include "log.hpp"

using std::cout;
using std::endl;

static const int N = 5;

template <class T>
class ThreadPool
{
private:
    ThreadPool(const int num = N) : _num(num)
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
    }

    ThreadPool(const ThreadPool<T> &tp) = delete;
    ThreadPool &operator=(const ThreadPool<T> &tp) = delete;

public:
    static ThreadPool<T> *getInstance()
    {
        if (_instance == nullptr)//因为创建线程池只有一次，加锁也只有一次，这么做可以减少加锁次数，提高效率
        {
            LockGuard lock(&_instance_mutex);
            if (_instance == nullptr)
            {
                //std::cout << "创建线程池单例" << std::endl;
                logMessage(Debug, "创建线程池单例");
                _instance = new ThreadPool<T>();
                _instance->init();
                _instance->start();
            }
        }
        return _instance;
    }

    pthread_mutex_t *getMutex()
    {
        return &_mutex;
    }

    void threadWait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    void threadWakeup()
    {
        pthread_cond_signal(&_cond);
    }

    bool isEmpty()
    {
        return _tasks.empty();
    }

    T popTask()
    {
        T t = _tasks.front();
        _tasks.pop();
        return t;
    }

    static void threadRoutine(void *args)
    {
        ThreadPool<T> *tp = static_cast<ThreadPool<T> *>(args);
        while (true)
        {
            // 检测是否有任务
            T t;
            {
                LockGuard lock(tp->getMutex());
                while (tp->isEmpty())
                {
                    tp->threadWait();
                }
                t = tp->popTask();
            }

            t();
        }
    }

    void init()
    {
        for (int i = 0; i < _num; i++)
        {
            _threads.push_back(Thread(i, threadRoutine, this));
            //std::cout << i << " thread running" << std::endl;
            logMessage(Debug, "%d thread running", i);
        }
    }

    void start()
    {
        for (auto &t : _threads)
        {
            t.run();
        }
    }

    void check()
    {
        for (auto &t : _threads)
        {
            cout << t.name() << " runnning..." << endl;
        }
    }

    void pushTask(const T &t)
    {
        // 加锁
        LockGuard lock(&_mutex);
        _tasks.push(t);
        threadWakeup();
    }
    ~ThreadPool()
    {
        for (auto &t : _threads)
        {
            t.join();
        }

        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

private:
    std::vector<Thread> _threads;
    int _num;

    std::queue<T> _tasks;

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;

    static ThreadPool<T> *_instance;
    static pthread_mutex_t _instance_mutex;
};

template <class T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;

template <class T>
pthread_mutex_t ThreadPool<T>::_instance_mutex = PTHREAD_MUTEX_INITIALIZER;